package cate.game.pvp.championrace;

import cate.common.table.d.GD;
import cate.common.table.d.GDFunc;
import cate.common.table.d.GDMail;
import cate.common.table.d.GDObj;
import cate.common.util.GameResult;
import cate.common.util.TimeTool;
import cate.game.db.facade.KvLoader;
import cate.game.db.mongo.kv.KvMongo;
import cate.game.event.championrace.ChampionRaceTimerEvent;
import cate.game.framework.ModuleMan;
import cate.game.framework.advanced.AdvancedLongAttributes;
import cate.game.mail.po.MailBuilder;
import cate.game.play.part.PlaySide;
import cate.game.pvp.championrace.data.*;
import cate.game.pvp.championrace.dto.ChampionRaceManyPsData;
import cate.game.pvp.championrace.dto.ChampionRaceRankRewardData;
import cate.game.pvp.championrace.dto.ChampionRaceSpecPlayersData;
import cate.game.res.MixRes;
import cate.game.res.MixResItem;
import cate.game.role.Role;
import cate.game.role.mail.Mail;
import cate.game.role.pvp.championrace.msg.ChampionRaceInvitationResp;
import cate.game.role.pvp.championrace.msg.ChampionRaceSpecResp;
import com.google.common.collect.Lists;
import cp.solution.business.game.component.NoticeParam;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static cate.game.client.msg.MsgSender.send;
import static cate.game.pvp.championrace.ChampionRaceSpecUtils.isBetRwd;
import static cate.game.pvp.championrace.ChampionRaceSpecUtils.isReadyStep;

public class GameChampionRaceDataManager extends ModuleMan {
    @NoteField("在比赛过程中变更战力导致ps脏了")
    private final Set<String> dirtyRoles = new HashSet<>();
    private GameChampionRaceKV data;

    @Override
    public void initDependent() throws Exception {
        data = new KvLoader<>(game, GameChampionRaceKV.class, KvMongo.KEY_CHAMPION_RACE).getBySrvId();
    }

    public void iamdirty(String roleUid) {
        if (isOpen() && TimeTool.isDayOfWeek(Calendar.SUNDAY) && getSpec().getPeriod() < 5) {
            synchronized (dirtyRoles) {
                dirtyRoles.add(roleUid);
            }
        }
    }

    private boolean ifDirty(String roleUid) {
        synchronized (dirtyRoles) {
            return dirtyRoles.contains(roleUid);
        }
    }

    @Override
    public void shutdown() {
        game.db.kv.save(data);
    }

    public void updateToNew(ChampionRaceSpec spec, long openTime, long closeTime, AdvancedLongAttributes attributes, boolean updateAttributes) {
        synchronized (dirtyRoles) {
            dirtyRoles.clear();
        }
        data.updateToNew(spec, openTime, closeTime, attributes, updateAttributes);
    }

    public int getSeason() {
        return data.getSeason();
    }

    public long getOpenTime() {
        return data.getOpenTime();
    }

    public ChampionRaceSpec getSpec() {
        return data.getSpec();
    }

    public void onOpenEvent(ChampionRaceTimerEvent.OpenEvent openEvent) {
        boolean settle = data.onOpenEvent(openEvent);
        if (settle) {
            synchronized (dirtyRoles) {
                dirtyRoles.clear();
            }
            game.role.activeForOnline(e -> e.getPvp().championRace.checkSeason());
        }
    }

    private boolean isOpen() {
        return game.getOpenDay() >= 8;
    }

    public void onRankSettleEvent(ChampionRaceTimerEvent.RankSettleEvent rankSettleEvent, GameChampionRaceMsgManager message) {
        boolean settle = data.onRankSettleEvent(rankSettleEvent);
        if (!settle || !isOpen()) {
            return;
        }
        ChampionRaceManyPsData psData = message.getManyPs(game.config.srvId);
        if (psData == null || CollectionUtils.isEmpty(psData.getList())) {
            return;
        }
        List<ChampionRaceOpponent> queryList = psData.getList();
        queryList.stream()
                .map(opp -> opp.getSnapshot().base.uid)
                .forEach(e -> handleSyncPosition(e, rankSettleEvent.getSpec(), message));
    }

    protected void handleSyncPosition(String uid, ChampionRaceSpec spec, GameChampionRaceMsgManager message) {
        Role role = game.role.getRole(uid);
        if (Objects.isNull(role)) {
            return;
        }
        EcResult<PlaySide> r = game.fight.side.createPlaySideByRole(role, GDFunc.CHAMPION_RACE, 1);
        if (r.ok() && Objects.nonNull(r.data)) {
            PlaySide ps = r.data;
            ps.snapshot.activeTime = 0;
            GameResult<ChampionRacePs> psr = ChampionRacePs.compressEc(ps, 0);
            if (psr.ok() && Objects.nonNull(psr.data)) {
                message.updatePs(spec, psr.data);
            }
        }
    }

    public void onInvitationEvent(ChampionRaceTimerEvent.InvitationEvent invitationEvent, GameChampionRaceMsgManager message) {
        boolean settle = data.onInvitationEvent(invitationEvent);
        if (!settle || !isOpen()) {
            return;
        }
        queryInvitedOpponentsAndHandle(message);
    }

    protected void queryInvitedOpponentsAndHandle(GameChampionRaceMsgManager message) {
        ChampionRaceManyPsData psData = message.getManyPs(game.config.srvId);
        if (psData == null || CollectionUtils.isEmpty(psData.getList())) {
            return;
        }
        psData.getList().forEach(this::sendInvitation);
    }

    private void sendInvitation(ChampionRaceOpponent opponent) {
        Role role = game.role.findOnline(opponent.getSnapshot().base.uid);
        if (Objects.nonNull(role)) {
            send(role::sendNow, new ChampionRaceInvitationResp(opponent.getPrRank()));
        }
    }

    public void onTrialEvent(ChampionRaceTimerEvent.TrialEvent trialEvent, GameChampionRaceMsgManager message) {
        boolean settle = data.onTrialEvent(trialEvent);
        if (!settle || !isOpen()) {
            return;
        }
        updatePositionAndHandle(trialEvent.getSpec(), message);
        game.role.activeForOnline(role -> {
            queryBetRecordAndHandle(role, trialEvent.getSpec(), message);
            noticeUpdateSpec(role, trialEvent.getSpec());
        });
    }

    public void onKoEvent(ChampionRaceTimerEvent.KoEvent koEvent, GameChampionRaceMsgManager message) {
        boolean settle = data.onKoEvent(koEvent);
        if (!settle || !isOpen()) {
            return;
        }
        updatePositionAndHandle(koEvent.getSpec(), message);
        game.role.activeForOnline(role -> {
            queryBetRecordAndHandle(role, koEvent.getSpec(), message);
            noticeUpdateSpec(role, koEvent.getSpec());
        });
    }

    public void onFinalEvent(ChampionRaceTimerEvent.FinalEvent finalEvent, GameChampionRaceMsgManager message) {
        boolean settle = data.onFinalEvent(finalEvent);
        if (!settle || !isOpen()) {
            return;
        }
        updatePositionAndHandle(finalEvent.getSpec(), message);
        game.role.activeForOnline(role -> {
            queryBetRecordAndHandle(role, finalEvent.getSpec(), message);
            noticeUpdateSpec(role, finalEvent.getSpec());
        });
    }

    protected void updatePositionAndHandle(ChampionRaceSpec spec, GameChampionRaceMsgManager message) {
        if (!isReadyStep(spec)) {
            return;
        }
        ChampionRaceSpecPlayersData playersData = message.getSpecPlayer(game.config.srvId, spec);
        if (playersData == null || MapUtils.isEmpty(playersData.players)) {
            return;
        }
        Map<String, String> queryMap = playersData.players;
        List<ChampionRacePs> psList = queryMap.entrySet().stream()
                .filter(e -> ifDirty(e.getKey()))
                .map(this::updatePosition)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (!org.springframework.util.CollectionUtils.isEmpty(psList)) {
            psList.forEach(cps -> message.updatePs(spec, cps));
        }
    }

    private ChampionRacePs updatePosition(Map.Entry<String, String> entry) {
        String uid = entry.getKey();
        String md5 = entry.getValue();
        Role role = game.role.getRole(uid);
        if (Objects.isNull(role)) {
            return null;
        }
        EcResult<PlaySide> r = game.fight.side.createPlaySideByRole(role, GDFunc.CHAMPION_RACE, 1);
        if (!r.ok() || Objects.isNull(r.data)) {
            return null;
        }
        EcResult<ChampionRacePs> psr = ChampionRacePs.compressEc(r.data, 0);
        if (!psr.ok() || Objects.isNull(psr.data)) {
            return null;
        }
        ChampionRacePs cps = psr.data;
        if (StringUtils.endsWithIgnoreCase(cps.getCpsList().getLast().getMd5Sum(), md5)) {
            return null;
        }
        return cps;
    }

    public void onRankSwitchEvent(ChampionRaceTimerEvent.RankSwitchEvent rankSwitchEvent, GameChampionRaceMsgManager message) {
        boolean settle = data.onRankSwitchEvent(rankSwitchEvent);
        if (!settle || !isOpen()) {
            log.info("无需发送 {} 服 [王者赛] 奖励", game.config.srvId);
            return;
        }
        log.info("开始发送 {} 服 [王者赛] 奖励", game.config.srvId);
        queryRankRwdAndHandle(message);
    }

    public void onShowEvent(ChampionRaceTimerEvent.ShowEvent showEvent) {
        boolean settle = data.onShowEvent(showEvent);
        if (!settle) {
            return;
        }
        synchronized (dirtyRoles) {
            dirtyRoles.clear();
        }
        game.role.activeForOnline(role -> noticeUpdateSpec(role, showEvent.getSpec()));
    }

    public void onRestEvent(ChampionRaceTimerEvent.RestEvent restEvent) {
        boolean settle = data.onRestEvent(restEvent);
        if (!settle || !isOpen()) {
            return;
        }
        game.role.activeForOnline(role -> noticeUpdateSpec(role, restEvent.getSpec()));
    }

    protected void noticeUpdateSpec(Role role, ChampionRaceSpec spec) {
        send(role::sendNow, new ChampionRaceSpecResp(spec));
    }

    protected void queryRankRwdAndHandle(GameChampionRaceMsgManager message) {
        ChampionRaceRankRewardData rankRwdData = message.getRankReward(data.getSeason(), game.config.srvId);
        if (rankRwdData == null) {
            log.error("跨服冠军赛 {} 服 排行榜奖励为空!", game.config.srvId);
            return;
        }
        log.info("开始发送 [跨服冠军赛] {} 服 排名邮件奖励!", game.config.srvId);
        for (ChampionRaceRankRwd rwd : rankRwdData.list) {
            int rank = rwd.getRank();
            String rewardStr = game.table.championRace.reward.getReward(rank);
            if (StringUtils.isBlank(rewardStr)) {
                continue;
            }
            int starMax = game.pvp.status.uidToStarMax.getOrDefault(rwd.getUid(), 0);
            int extra = game.table.pvpRankPointExtra.base.getExtraParam(starMax);
            Mail mail;
            if (extra > 0) {
                MixRes res = new MixRes(rewardStr);
                long count = res.count(GDObj.Type.NUM, GDObj.Num.CHAMPION_RACE_POINT);
                long addNum = count * extra / GD.W_10000;
                MixResItem item = new MixResItem(GDObj.Type.NUM, GDObj.Num.CHAMPION_RACE_POINT, addNum);
                res.addItem(item);
                mail = new MailBuilder()
                        .setContentId(GDMail.Tid.CHAMPION_RANK_REWARD_EX)
                        .setNoticeParamList(Lists.newArrayList(
                                new NoticeParam(rwd.getRank()),
                                new NoticeParam(starMax),
                                new NoticeParam((extra / 100.0) + "%"),
                                new NoticeParam(NoticeParam.NoticeType.MIX_RES, new MixRes().addItem(item))))
                        .setReward(res)
                        .build();
            } else {
                mail = new MailBuilder()
                        .setContentId(GDMail.Tid.CHAMPION_RANK_REWARD)
                        .setParamList(Lists.newArrayList(rwd.getRank()))
                        .setReward(new MixRes(rewardStr))
                        .build();
            }
            game.mail.send(rwd.getUid(), mail);
            Role role = game.role.getRole(rwd.getUid());
            if (Objects.nonNull(role)) {
                log.info("玩家:{} uid:{} 排名:{}", role.getBase().name, role.getBase().uid, rwd.getRank());
                role.getPvp().championRace.refreshRecord(rwd.getRank());
            } else {
                log.error("找不到对应的玩家: {}", rwd.getUid());
            }
        }
        log.info("结束发送 [王者赛] 排名邮件奖励!");
    }

    protected void queryBetRecordAndHandle(Role role, ChampionRaceSpec spec, GameChampionRaceMsgManager message) {
        if (isBetRwd(spec)) {
            ChampionRaceSpec querySpec = ChampionRaceSpec.ofNullStep(spec);
            ChampionRaceBetRecord betRecord = message.getBetRecord(querySpec);
            if (betRecord != null) {
                handleBetRwd(role, betRecord);
            }
        }
    }

    protected void handleBetRwd(Role role, ChampionRaceBetRecord betRecord) {
        role.getPvp().championRace.handleBetRwd(betRecord);
    }
}
